home *** CD-ROM | disk | FTP | other *** search
- /*
- TestClut.c
-
- This is used by TimeVideo; I can't think of any reason for including it in
- any other program.
-
- OSErr TestClut(FILE *o[2],GDHandle device,short flags,VideoCard *card);
-
- Tests whether the video clut can be written and read faithfully. This tests for
- bad clut memory, and whether the clut is actually being loaded with what we
- sent. A surprisingly large fraction of video drivers fail this test, for various
- reasons. The test consists of writing random numbers to all the clut entries,
- reading them back, and comparing.
-
- The bits of "flags" are tested independently. If flags&testClutQuicklyFlag then
- SetEntriesQuickly() will be tested instead of GDSetEntries/GDDirectSetEntries.
- If flags&testClutSeriallyFlag then the clut entries will be set individually,
- calling GDSetEntries once for each clut entry, to check the clut entry
- addressing. If flags&testClutLinearFlag then a simple sequence, will be loaded
- into the clut, instead of random numbers, to help figuring out systematic
- errors. The sequence is (0,0,0),(1,0,0), (0,2,0),(0,0,3),(4,4,4),(4,0,0), and so
- on.
-
- Returned value is zero if ok, nonzero if error occurred.
-
- TestClut tries to recognize common kinds of driver error and report them
- in a sensible way, using the various fields of the card->depth[m].clut structure.
- Errors accumulate in the card->depth[m].clut structure, allowing you to make
- multiple calls to TestClut and only then summarize the results. It is important
- that you zero card->depth[m].clut.tests and card->depth[m].clutQuickly.tests
- before your first call to TestClut, to induce it to zero the rest of the
- clut structures.
-
- Assumes that GDevice record is valid, i.e. the user has not called GDSetMode().
- HISTORY:
- 3/9/93 dgp code extracted from the demo TestCluts.c, to create a reusable
- subroutine.
- 3/10/93 dgp No longer assume that GDevice record reflects the actual state of the
- driver.
- */
- #include "VideoToolbox.h"
- #include <Errors.h>
- #include "GDInfo.h"
-
- // These functions are solely for use within this file.
- ColorSpec *MakeClutTable(GDHandle device,short flags,short dacMask);
- OSErr WriteClut(GDHandle device,ColorSpec table[],short flags);
- int CountClutErrors(GDHandle device,ColorSpec table[],ColorSpec table2[]
- ,short flags,VideoCard *card);
- void ReportClutErrors(FILE *file,GDHandle device,ColorSpec table[],ColorSpec table2[]
- ,short flags,VideoCard *card);
- OSErr ShowGammaTable(FILE *o[2],GDHandle device);
- void RGBToGray(RGBColor *rgb,short dacSize);
- Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask);
- OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
- ,Boolean *hashPtr);
- #define GAMMA_TABLE 0 // Enable only for debugging.
-
- OSErr TestClut(FILE *o[2],GDHandle device,short flags,VideoCard *card)
- {
- short i,colors,errors;
- OSErr error;
- ColorSpec spec,*table,*table2;
- SetEntriesFunction function;
- VideoCardClutTest *clut;
-
- clut=&card->depth[card->m].clut;
- if(clut->tests==0){
- clut->errors=0;
- clut->color.errors=0;
- clut->gray.errors=0;
- clut->color.zeroStartErrors=0;
- clut->gray.zeroStartErrors=0;
- }
- if(clut->visual.tests==0){
- clut->visual.errors=0;
- clut->visual.zeroStartErrors=0;
- }
- clut=&card->depth[card->m].clutQuickly;
- if(clut->tests==0){
- clut->errors=0;
- clut->color.errors=0;
- clut->gray.errors=0;
- clut->color.zeroStartErrors=0;
- clut->gray.zeroStartErrors=0;
- }
- if(clut->visual.tests==0){
- clut->visual.errors=0;
- clut->visual.zeroStartErrors=0;
- }
- GDInfo(device,card);
- colors=card->depth[card->m].colors;
- #if GAMMA_TABLE
- ShowGammaTable(o,device);
- #endif
- error=GDSaveGamma(device);
- error=GDUncorrectedGamma(device);
- if(error)return error;
- #if GAMMA_TABLE
- ShowGammaTable(o,device);
- #endif
-
- // MAKE TABLE FOR CLUT
- table=MakeClutTable(device,flags,card->dacMask);
- if(table==NULL)return MemError();
-
- // WRITE IT TO CLUT
- error=WriteClut(device,table,flags);
- if(error)return error;
-
- // MAKE BLANK TABLE
- table2=(ColorSpec *)NewPtr(sizeof(*table)*colors);
- if(table2==NULL)return MemError();
- spec.value=spec.rgb.red=spec.rgb.green=spec.rgb.blue=0;
- for(i=0;i<colors;i++)table2[i]=spec;
-
- // READ CLUT INTO BLANK TABLE
- error=GDGetEntries(device,0,colors-1,table2);
- if(!error){
- // COMPARE
- errors=CountClutErrors(device,table,table2,flags,card);
- if(errors)ReportClutErrors(o[1],device,table,table2,flags,card);
- card->clutTest=1;
- }
-
- DisposePtr((Ptr)table);
- DisposePtr((Ptr)table2);
- GDRestoreGamma(device);
- GDRestoreDeviceClut(device);
-
- // if(error || card->visual)return TestClutVisually(device,flags,card);
- return 0;
- }
-
- OSErr TestClutVisually(GDHandle device,short flags,VideoCard *card)
- {
- short i,error,colors;
- char blankLine[]="\r "
- " \r";
- ColorSpec *table,*normalTable;
- Boolean weirdError,normalError;
- VideoCardClutTest *clut;
-
- if(flags&testClutQuicklyFlag)clut=&card->depth[card->m].clutQuickly;
- else clut=&card->depth[card->m].clut;
- if(clut->visual.tests==0){
- clut->visual.errors=0;
- clut->visual.zeroStartErrors=0;
- }
- colors=GDClutSize(device);
- normalTable=((**(**(**device).gdPMap).pmTable)).ctTable;
- if(device==GetMainDevice())
- table=MakeClutTable(device,testClutNegativeFlag,card->dacMask);
- else table=MakeClutTable(device,flags,card->dacMask);
- if(table==NULL)return MemError();
- GDSaveGamma(device);
- GDUncorrectedGamma(device);
- error=WriteClut(device,table,flags);
- if(error){
- GDRestoreGamma(device);
- GDRestoreDeviceClut(device);
- return error;
- }
- printf(blankLine);
- printf("Screen should be weirdly colored; watch for subtle change as you hit return:\r");
- while(getcharUnbuffered()==-1);
- error=WriteClut(device,table,0);
- if(error){
- GDRestoreGamma(device);
- GDRestoreDeviceClut(device);
- return error;
- }
- printf(blankLine);
- printf("Did you see any change at all?");
- weirdError=YesOrNo(0);
- printf("\r");
- error=WriteClut(device,normalTable,flags);
- printf(blankLine);
- printf("Screen should be normal now; watch for subtle change as you hit return:\r");
- while(getcharUnbuffered()==-1);
- error=WriteClut(device,normalTable,0);
- printf(blankLine);
- printf("Did you see any change at all?");
- normalError=YesOrNo(0);
- printf("\r");
- GDRestoreGamma(device);
- GDRestoreDeviceClut(device);
- DisposePtr((Ptr)table);
- clut->visual.tests++;
- clut->visual.errors+=(weirdError || normalError);
- if(!(flags&testClutSeriallyFlag))clut->visual.zeroStartErrors+=(weirdError || normalError);
- return 0;
- }
-
- ColorSpec *MakeClutTable(GDHandle device,short flags,short dacMask)
- {
- short shift,i,colors;
- RGBColor put;
- ColorSpec *table;
-
- colors=GDClutSize(device);
- shift=16-Log2L(colors);
- table=(ColorSpec *)NewPtr(sizeof(*table)*colors);
- if(table==NULL)return table;
- for(i=0;i<colors;i++) {
- if(flags&testClutNegativeFlag){
- put=((**(**(**device).gdPMap).pmTable)).ctTable[colors-1-i].rgb;
- }else if(flags&testClutLinearFlag){
- // Linear test pattern
- put.red=put.green=put.blue=0;
- switch(i%4){
- case 0:
- put.red=put.green=put.blue=i<<shift;
- break;
- case 1:
- put.red=i<<shift;
- break;
- case 2:
- put.green=i<<shift;
- break;
- case 3:
- put.blue=i<<shift;
- break;
- }
- }else{
- // Random test pattern
- put.red=randU();
- put.green=randU();
- put.blue=randU();
- }
- put.red&=dacMask;
- put.green&=dacMask;
- put.blue&=dacMask;
- table[i].rgb=put;
- }
- return table;
- }
-
- Boolean UnequalClutEntry(RGBColor *a,RGBColor *b,short mask)
- {
- return (a->red&mask)!=(b->red&mask)
- ||(a->green&mask)!=(b->green&mask)
- ||(a->blue&mask)!=(b->blue&mask);
- }
-
- int CountClutErrors(GDHandle device,ColorSpec table[],ColorSpec table2[]
- ,short flags,VideoCard *card)
- {
- short errors,i,m,colors;
- short colorErrors,grayErrors;
- RGBColor put,gray,got;
- VideoCardClutTest *clut;
-
- m=card->m;
- colors=card->depth[m].colors;
- colorErrors=grayErrors=0;
- if(flags&testClutQuicklyFlag) clut=&card->depth[m].clutQuickly;
- else clut=&card->depth[m].clut;
- for(i=0;i<colors;i++) {
- gray=put=table[i].rgb;
- got=table2[i].rgb;
- RGBToGray(&gray,card->dacSize);
- if(UnequalClutEntry(&put,&got,card->dacMask))colorErrors++;
- if(UnequalClutEntry(&gray,&got,card->dacMask))grayErrors++;
- clut->tests++;
- }
- clut->color.errors+=colorErrors;
- clut->gray.errors+=grayErrors;
- if(!(flags&testClutSeriallyFlag)){
- clut->color.zeroStartErrors+=colorErrors;
- clut->gray.zeroStartErrors+=grayErrors;
- }
- if(card->isGray)errors=grayErrors;
- else errors=colorErrors;
- clut->errors+=errors;
- return errors;
- }
-
- void ReportClutErrors(FILE *file,GDHandle device,ColorSpec table[],ColorSpec table2[]
- ,short flags,VideoCard *card)
- {
- short errors,i,colors=card->depth[card->m].colors;
- short colorError,colorErrors,grayError,grayErrors;
- RGBColor put,gray,got;
-
- // Print one-line error message to file
- fprintf(file,"\n");
- if(flags&testClutQuicklyFlag)fprintf(file,"SetEntriesQuickly != GDGetEntries");
- else switch(card->gdType){
- case fixedType:
- break;
- case clutType:
- fprintf(file,"GDSetEntries != GDGetEntries");
- break;
- case directType:
- fprintf(file,"GDDirectSetEntries != GDGetEntries");
- break;
- }
- if(flags&testClutSeriallyFlag)fprintf(file," (Loaded one clut entry at a time.)\n");
- else fprintf(file," (Loaded whole clut at once.)\n");
-
- // Count errors
- colorErrors=grayErrors=0;
- for(i=0;i<colors;i++) {
- gray=put=table[i].rgb;
- got=table2[i].rgb;
- RGBToGray(&gray,card->dacSize);
- if(UnequalClutEntry(&put,&got,card->dacMask))colorErrors++;
- if(UnequalClutEntry(&gray,&got,card->dacMask))grayErrors++;
- }
- if(colorErrors==0 || grayErrors==0)return;
-
- // Print each error to file
- for(i=0;i<colors;i++) {
- gray=put=table[i].rgb;
- got=table2[i].rgb;
- RGBToGray(&gray,card->dacSize);
- colorError=UnequalClutEntry(&put,&got,card->dacMask);
- grayError=UnequalClutEntry(&gray,&got,card->dacMask);
- if(!card->isGray && colorError || card->isGray && grayError){
- fprintf(file,"Clut[%3d] wrote(0x%04x,0x%04x,0x%04x) "
- "(luminance 0x%04x) but read(0x%04x,0x%04x,0x%04x)\n"
- ,i,put.red,put.green,put.blue,gray.red,got.red,got.green,got.blue);
- }
- }
- }
-
- OSErr WriteClut(GDHandle device,ColorSpec table[],short flags)
- {
- short i,colors=GDClutSize(device);
- char priority=7;
- OSErr error;
- SetEntriesFunction function;
-
- if(flags&testClutQuicklyFlag)function=SetEntriesQuickly;
- else function=GDSetEntriesByType;
- if(flags&testClutSeriallyFlag){
- // Load one clut entry at a time
- for(i=0;i<colors;i++){
- SwapPriority(&priority); // Force driver to load clut now.
- error=(function)(device,i,0,&table[i]);
- SwapPriority(&priority); // Force driver to load clut now.
- if(error)return error;
- }
- }else{
- // Load whole clut at once
- SwapPriority(&priority); // Force driver to load clut now.
- error=(function)(device,0,colors-1,table);
- SwapPriority(&priority); // Force driver to load clut now.
- if(error)return error;
- }
- }
-
- OSErr ShowGammaTable(FILE *o[2],GDHandle device)
- {
- OSErr error;
- unsigned char *byte;
- unsigned short *word;
- int i,j,identity;
- GammaTbl *gamma;
-
- if((**device).gdType==fixedType)return statusErr;
- error=GDGetGamma(device,&gamma);
- if(error){
- ffprintf(o,"GetGamma: GDGetGamma() error %d\n",error);
- if(error==statusErr)
- ffprintf(o,"The video driver doesn't support this call.\n");
- return error;
- }
- byte=(unsigned char *)gamma->gFormulaData+gamma->gFormulaSize;
- word=(unsigned short *)byte;
- identity=1;
- if(gamma->gDataWidth<=8)
- for(i=0;i<gamma->gDataCnt;i++)identity &= (i==byte[i]);
- else
- for(i=0;i<gamma->gDataCnt;i++)identity &= (i==word[i]);
- if(identity){
- ffprintf(o,"Gamma Table: identity transformation\n");
- }else{
- ffprintf(o,"Gamma Table:\n");
- ffprintf(o,"at 0x%lx,gDataWidth %d,gDataCnt %d,gVersion %d,gType %d,gFormulaSize %d,gChanCnt %d\n"
- ,gamma,gamma->gDataWidth,gamma->gDataCnt,gamma->gVersion,gamma->gType,gamma->gFormulaSize,gamma->gChanCnt);
- for(i=0;i<gamma->gDataCnt;i+=64) {
- ffprintf(o,"%3d: ",i);
- if(gamma->gDataWidth<=8)
- for(j=0;j<16;j++) ffprintf(o," %3u",byte[i+j]);
- else
- for(j=0;j<16;j++) ffprintf(o," %3u",word[i+j]);
- ffprintf(o,"\n");
- }
- }
- return 0;
- }
- /*
- When you set a video screen to monochrome or "gray" (as opposed to "color"),
- e.g. using the Control Panel:Monitors, the request is passed on to the video
- driver. The video driver transforms each of your rgb triplets to a
- luminance-equivalent gray, using a formula that must be very similar, if not
- equivalent, to the code below. The rounding is bad, e.g. any gray rgb triplet
- (i,i,i), other than (0,0,0), is transformed to (i-1,i-1,i-1), which is darker,
- failing to preserve luminance. However, my goal was to replicate Apple's crumby
- transformation, not to improve it. I presume that the reason that I have to trim
- my numbers down a tad (-0.00001) is that I'm doing this with 80-bit precision
- whereas the driver uses the 64-bit precision of the SANE routines. Presumably I
- could obtain the same result by compiling this subroutine separately, to use
- 64-bit floating point, since all the arguments are ints.
- */
- void RGBToGray(RGBColor *rgb,short dacSize)
- // Empirical formula to replicate Apple's luminance mapping.
- {
- short i,shift;
- unsigned long n;
-
- shift=16-dacSize;
- i=(rgb->red>>shift)*(0.30-0.00001)+(rgb->green>>shift)*(0.59-0.00001)+(rgb->blue>>shift)*(0.11);
- n=0xffffffffUL/((1<<dacSize)-1);
- rgb->red=rgb->green=rgb->blue=(i*n)>>16;
- }
-
- OSErr TestClutHash(GDHandle device,SetEntriesFunction function,VideoCardClutTest *clut)
- {
- OSErr error;
-
- if(clut->hashTest){
- error=VisibleHash(device,function,0,&clut->hash);
- }else error=0;
- return error;
- }
-
- OSErr VisibleHash(GDHandle device,SetEntriesFunction function,short clutEntries
- ,Boolean *hashPtr)
- {
- OSErr error;
- short mode,clutSize,i;
- Boolean hash;
- long tick;
- ColorSpec *table,*linearTable=NULL;
- char string[100];
-
- clutSize=GDClutSize(device);
- if(clutEntries<0 || clutEntries>clutSize)return 1;
- if(clutEntries==0)clutEntries=clutSize;
- error=GDGetMode(device,&mode,NULL,NULL);
- if(error)mode=(**device).gdMode;
- if(mode>eightBitMode){
- if(function==GDSetEntries)function=GDDirectSetEntries;
- table=linearTable=(ColorSpec *)NewPtr(clutSize*sizeof(linearTable[0]));
- if(linearTable==NULL)return MemError();
- for(i=0;i<clutSize;i++){
- table->rgb.red=table->rgb.green=table->rgb.blue=(long)0xffff*i/(clutSize-1);
- table++;
- }
- }else table=((**(**(**device).gdPMap).pmTable)).ctTable;
- error=(function)(device,0,clutEntries-1,table);
- if(!error){
- printf(" \r");
- printf("Do you see any hash on the test screen? (No):");
- do{
- tick=TickCount();
- do{
- (function)(device,0,clutEntries-1,table);
- }while(tick==TickCount());
- }while(!kbhit());
- hash=YesOrNo(0);
- printf("\r");
- if(linearTable!=NULL)DisposePtr((Ptr)linearTable);
- if(hashPtr!=NULL)*hashPtr=hash;
- }
- return error;
- }
-
-